﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VAdvantage.ProcessEngine;
using System.Data.SqlClient;
using System.Data;
using VAdvantage.WF;
using System.Reflection;
using VAdvantage.DataBase;
using VAdvantage.Utility;
using System.Data.OracleClient;
using VAdvantage.Print;
using System.Threading;
using System.Windows.Forms;

namespace VAdvantage.ProcessEngine
{
    public class ProcessCtl
    {
        Ctx _ctx = null;

        /// <summary>
        /// Start Workflow.
        /// </summary>
        /// <param name="AD_Workflow_ID">AD_Workflow_ID </param>
        /// <returns>true if started</returns>
        private bool StartWorkflow(int AD_Workflow_ID)
        {
            //Remote process not implemented
            //Only local process is implemented
            //log.Fine(AD_Workflow_ID + " - " + _pi.ToString());
            bool started = false;
            //	Run locally
            if (!started)
            {
                MWorkflow WF = new MWorkflow(_ctx, AD_Workflow_ID, null);
                MWFProcess wfProcess = null;
                if (_pi.IsBatch())
                    wfProcess = WF.Start(_pi);		//	may return null
                else
                    wfProcess = WF.StartWait(_pi);	//	may return null

                started = wfProcess != null;
            }

            return started;

        }

        /// <summary>
        /// Start the DB Process
        /// </summary>
        /// <param name="procedureName">name of the process</param>
        /// <returns></returns>
        private bool StartDBProcess(String procedureName)
        {
            if (DatabaseType.IsPostgre)  //jz Only DB2 not support stored procedure now
            {
                return false;
            }

            //  execute on this thread/connection
            //String sql = "{call " + procedureName + "(" + _pi.GetAD_PInstance_ID() + ")}";
            try
            {
                //only oracle procedure are supported
                OracleCommand comm = new OracleCommand();
                OracleConnection conn = (OracleConnection)DataBase.DB.GetConnection();
                conn.Open();
                comm.Connection = conn;
                comm.CommandText = procedureName;
                comm.CommandType = CommandType.StoredProcedure;
                OracleCommandBuilder.DeriveParameters(comm);
                OracleParameter[] param = new OracleParameter[1];
                foreach (OracleParameter orp in comm.Parameters)
                {
                    param[0] = new OracleParameter(orp.ParameterName, _pi.GetAD_PInstance_ID());
                }

                //log.Fine("Executing " + procedureName + "(" + _pi.GetAD_PInstance_ID() + ")");
                int res = SqlExec.Oracle.OracleHelper.ExecuteNonQuery(conn, CommandType.StoredProcedure, procedureName, param);
                //DataBase.DB.ExecuteQuery(sql, null);
            }
            catch (Exception e)
            {
                //log.Log(Level.SEVERE, "Error executing procedure " + procedureName, e);
                _pi.SetSummary(Msg.GetMsg(Env.GetContext(), "ProcessRunError") + " " + e.Message);
                _pi.SetError(true);
                return false;

            }
            //	log.fine(Log.l4_Data, "ProcessCtl.startProcess - done");
            return true;
        }

        ProcessInfo _pi = null;

        volatile ReportEngine_N re = null;

        private void Unlock()
        {
            String summary = _pi.GetSummary();
            if (summary != null && summary.IndexOf("@") != -1)
                _pi.SetSummary(Utility.Msg.ParseTranslation(Utility.Env.GetContext(), summary));
        }

        public Dictionary<string, object> Process(ProcessInfo pi, Ctx ctx, out byte[] report, out ReportEngine_N _rep)
        {
            _rep = null;

            _ctx = ctx;
            _pi = pi;
            report = null; 

            MPInstance instance = null;

            if (_pi.GetAD_PInstance_ID() < 1)
            {
                try
                {
                    instance = new MPInstance(Utility.Env.GetContext(), _pi.GetAD_Process_ID(), _pi.GetRecord_ID());
                }
                catch (Exception e)
                {
                    _pi.SetSummary(e.Message);
                    _pi.SetError(true);
                    return _pi.ToList();
                }

                if (!instance.Save())
                {
                    _pi.SetSummary(Msg.GetMsg(Env.GetContext(), "ProcessNoInstance", true));
                    _pi.SetError(true);
                    return _pi.ToList();
                }

                _pi.SetAD_PInstance_ID(instance.Get_ID());
            }



            String procedureName = "";
            int AD_ReportView_ID = 0;
            int AD_Workflow_ID = 0;
            bool IsReport = false;
            bool IsDirectPrint = false;

            String sql = "SELECT p.Name, p.procedureName,p.Classname, p.AD_Process_ID,"		//	1..4  
                + " p.IsReport,p.IsDirectPrint,p.AD_ReportView_ID,p.AD_Workflow_ID,"		//	5..8
                + " CASE WHEN COALESCE(p.Statistic_Count,0)=0 THEN 0 ELSE p.Statistic_Seconds/p.Statistic_Count END,"
                + " p.IsServerProcess "
                + "FROM AD_Process p"
                + " INNER JOIN AD_PInstance i ON (p.AD_Process_ID=i.AD_Process_ID) "
                + "WHERE p.IsActive='Y'"
                + " AND i.AD_PInstance_ID=@pinstanceid";

            IDataReader dr = null;
            try
            {
                SqlParameter[] param = new SqlParameter[1];
                param[0] = new SqlParameter("@pinstanceid", _pi.GetAD_PInstance_ID());
                dr = SqlExec.ExecuteQuery.ExecuteReader(sql, param);
                while (dr.Read())
                {
                    _pi.SetTitle(dr[0].ToString());
                    //if (m_waiting != null)
                    //    m_waiting.setTitle(_pi.getTitle());
                    procedureName = dr[1].ToString();

                    _pi.SetClassName(dr[2].ToString());

                    _pi.SetAD_Process_ID(Utility.Util.GetValueOfInt(dr[3].ToString()));
                    //	Report
                    if ("Y".Equals(dr[4].ToString()))
                    {
                        IsReport = true;
                        //later
                    }
                    AD_ReportView_ID = dr[6].ToString() == "" ? 0 : Utility.Util.GetValueOfInt(dr[6].ToString());
                    AD_Workflow_ID = dr[7].ToString() == "" ? 0 : Utility.Util.GetValueOfInt(dr[7].ToString());
                    //
                    //_IsServerProcess = "Y".Equals(dr[9].ToString());

                }
                dr.Close();
            }
            catch (Exception ex)
            {
                _pi.SetSummary(Msg.GetMsg(Env.GetContext(), "ProcessNoProcedure") + " " + ex.Message, true);
                Unlock();

            }

            if (procedureName == null)
                procedureName = "";

            if (AD_Workflow_ID > 0)
            {
                StartWorkflow(AD_Workflow_ID);
                //Updated by raghu to open reports from work flow
                byte[] repByt = null;
                re = ReportCtl.Report;
                if (re != null)
                {
                    int reportTable_ID = re.GetPrintFormat().GetAD_Table_ID();
                    re.GetView();
                    _pi.Set_AD_PrintFormat_Table_ID(re.GetPrintFormat().GetAD_Table_ID());
                    _pi.SetSummary("Report", re != null);
                    Unlock();
                    if (re != null)
                    {
                        repByt = re.CreatePDF();
                    }
                }
                else
                {
                    repByt = null;
                }
                report = repByt;
                return _pi.ToList();
            }

            if (_pi.GetClassName() != null)
            {
                if (!StartProcess())
                {
                    report = null;
                    return _pi.ToList();
                }

                if (!IsReport && procedureName.Length == 0)
                {
                    report = null;
                    return _pi.ToList();
                }
            }
            report = null;
            if (IsReport)
            {
                if (procedureName.Length > 0)
                {
                    if (!StartDBProcess(procedureName))
                    {
                    }
                }

                //start report code
                //	Start Report	-----------------------------------------------
                re = ReportCtl.Start(_ctx, _pi, IsDirectPrint);
                _rep = re;
                int reportTable_ID = re.GetPrintFormat().GetAD_Table_ID();
                re.GetView();
                _pi.Set_AD_PrintFormat_Table_ID(re.GetPrintFormat().GetAD_Table_ID());
                _pi.Set_AD_PrintFormat_ID(re.GetPrintFormat().GetAD_PrintFormat_ID());
                _pi.SetSummary("Report", re != null);
                Unlock();
                if (re != null)
                {
                    report = re.CreatePDF();
                }
            }
            else
            {
                if (!StartDBProcess(procedureName))
                {
                    _pi.SetSummary("procedure ERROR");
                    return _pi.ToList();
                }
                ProcessInfoUtil.SetSummaryFromDB(_pi);
            }

            return _pi.ToList();
        }


        public const string ASSEMBLY_NAME = "ModelLibrary";

        Trx _trx;

        /// <summary>
        /// Starts the process by calling the required class at run time.
        /// </summary>
        /// <returns>Returs ture or false on the successfull calling of the proecss</returns>
        private bool StartProcess()
        {
            //_trx = Trx.Get("ServerProcess", true);
            //log.Fine(_pi.ToString());
            try
            {
                // Assembly assembly = Assembly.LoadFrom(GlobalVariable.ASSEMBLY_NAME);
                string className = _pi.GetClassName().Replace("org.compiere.process", "VAdvantage.Process");
                className = className.Replace("org.compiere.wf", "VAdvantage.WF");
                className = className.Replace("org.compiere.report", "VAdvantage.Report");
                className = className.Replace("org.compiere.install", "VAdvantage.Install");
                className = className.Replace("org.compiere.print", "VAdvantage.Print");
                className = className.Replace("org.compiere.cmrp.process", "ViennaAdvantage.CMRP.Process");
                className = className.Replace("org.compiere.cmfg.process", "ViennaAdvantage.CMFG.Process");
                className = className.Replace("org.compiere.cwms.process", "ViennaAdvantage.CWMS.Process");
                className = className.Replace("org.compiere.cm", "VAdvantage.CM");

                /*Customization Process */

                Assembly asm = null;//asm = Assembly.Load(ASSEMBLY_NAME);
                Type type = null;
                
                /*********** Module ********************/

                Tuple<String,String> asmInfo = null;

                //string cName = className.Substring(className.LastIndexOf('.') + 1);
                string cName = _pi.GetTitle();

                if (VAdvantage.Utility.Env.HasModulePrefix(cName, out asmInfo))
                {
                    try
                    {
                        asm = Assembly.Load(asmInfo.Item1);
                        type = asm.GetType(className); 
                    }
                    catch
                    {
                    }
                }

                /***********  END   ********************/
                if (type == null)
                {
                    try
                    {
                        asm = Assembly.Load(GlobalVariable.PRODUCT_NAME);
                        type = asm.GetType(className);
                    }
                    catch
                    {
                        // asm = Assembly.Load(GlobalVariable.ASSEMBLY_NAME);
                    }

                }

               
                /*EndCustomization*/

                if (type == null)
                {
                    type = Type.GetType(className);
                }
                if (type == null)
                {
                    MessageBox.Show("no Type");
                }

                if (type.IsClass)
                {
                    ProcessEngine.ProcessCall oClass = (ProcessEngine.ProcessCall)Activator.CreateInstance(type);
                    if (oClass == null)
                        return false;
                    else
                        //oClass.StartProcess(Utility.Env.GetContext(), _pi, _trx);
                        oClass.StartProcess(_ctx, _pi, _trx);

                    if (_trx != null)
                    {
                        _trx.Commit();
                        //log.Fine("Commit " + _trx.ToString());
                        _trx.Close();
                    }

                }
            }
            catch (Exception ex)
            {
                if (_trx != null)
                {
                    _trx.Rollback();
                    //log.Fine("Rollback " + _trx.ToString());
                    _trx.Close();
                }
                _pi.SetSummary("Error starting Class " + _pi.GetClassName().Replace("org.compiere.process", "VAdvantage.Process"), true);
                //log.Log(Level.SEVERE, _pi.GetClassName(), ex);
            }
            return !_pi.IsError();
        }

    }
}
